<template>
  <el-form
    @submit.prevent
    :model="model"
    class="form-components"
    :class="{ 'block ': !_options.inline, line: _options.option }"
    v-bind="_options"
    ref="formRef"
  >
    <template
      v-for="(item, index) in fieldList"
      :key="`${index}${item.field || '-'}`"
    >
      <FormItem
        :label="item.label"
        v-if="item.type === 'radio'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <el-radio-group
          v-model="model[item.field]"
          :disabled="item.disabled"
          :style="item.style"
        >
          <el-radio
            v-for="val in originOptions?.[item.origin] || item.options?.data"
            :label="val[item.options?.valueKey || 'value']"
            :key="val[item.options?.valueKey || 'value']"
          >
            {{ val[item.options?.labelKey || 'label'] }}
          </el-radio>
        </el-radio-group>
      </FormItem>
      <div
        class="form-title"
        style="width: 100%"
        v-else-if="item.type === 'title'"
      >
        <FormTitle :title="item.label" />
      </div>
      <FormItem
        v-else-if="item.slot"
        :label="item.label"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :style="item.style"
        :show="getShow(item)"
      >
        <FormCombined
          v-if="item.select"
          :style="item.full || !_options.inline ? 'flex: 1' : ''"
          :selectStyle="item.selectStyle || 'width: 100px'"
          :options="item.select"
          :modelValue="model[item.field]"
          @update:modelValue="handleCombined(item, $event)"
        >
          <template #default="{ value, input }">
            <slot :name="item.slot" :value="value" :input="input"> </slot>
          </template>
        </FormCombined>
        <slot
          v-else
          :name="item.slot"
          :value="model[item.field]"
          :input="updateSlotValue(item.field)"
        >
        </slot>
      </FormItem>
      <FormItem
        v-else-if="item.type === 'numberRange'"
        :label="item.label"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <NumberRange
          :style="item.style"
          :value="model[item.field]"
          :suffix="item.suffix"
          :precision="item.precision"
          :min="item.min"
          :max="item.max"
          :placeholder="item.placeholder"
          @input="updateNumberRange(item.field, $event)"
        />
      </FormItem>
      <FormItem
        :label="item.label"
        v-else-if="item.type === 'checkbox'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <el-checkbox-group
          v-model="model[item.field]"
          :disabled="item.disabled"
          :style="item.style"
        >
          <el-checkbox
            v-for="c in originOptions?.[item.origin] || item.options?.data"
            :key="c[item.options?.valueKey || 'value']"
            :label="c[item.options?.valueKey || 'value']"
            >{{ c[item.options?.labelKey || 'label'] }}
          </el-checkbox>
        </el-checkbox-group>
      </FormItem>
      <FormItem
        :label="item.label"
        v-else-if="item.type === 'select'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <FormCombined
          v-if="item.select"
          :style="item.full || !_options.inline ? 'flex: 1' : ''"
          :selectStyle="item.selectStyle || 'width: 100px'"
          :options="item.select"
          :modelValue="model[item.field]"
          @update:modelValue="handleCombined(item, $event)"
        >
          <template #default="{ value, input }">
            <el-select
              :style="[
                item.full || !_options.inline ? { width: '100%' } : null,
                item.style,
              ]"
              :modelValue="value"
              @update:modelValue="input"
              :filterable="item.filterable ?? true"
              :clearable="item.clearable ?? true"
              :popper-append-to-body="false"
              :teleported="false"
              placement="bottom"
              :multiple="item.multiple"
              :collapseTags="item.collapseTags"
              :maxCollapseTags="
                item.collapseTags && !item.maxCollapseTags
                  ? 1
                  : item.maxCollapseTags
              "
              :collapseTagsTooltip="
                item.collapseTags && !item.collapseTagsTooltip
                  ? true
                  : item.collapseTagsTooltip
              "
              :placeholder="item.options?.placeholder || '请选择'"
              :disabled="item.disabled"
              v-bind="item.itemProps"
            >
              <el-option
                v-for="s in originOptions?.[item.origin] || item.options?.data"
                :key="s[item.options?.valueKey || 'value']"
                :label="s[item.options?.labelKey || 'label']"
                :value="s[item.options?.valueKey || 'value']"
              />
            </el-select>
          </template>
        </FormCombined>
        <el-select
          v-else
          :style="[
            item.full || !_options.inline ? { width: '100%' } : null,
            item.style,
          ]"
          v-model="model[item.field]"
          :filterable="item.filterable ?? true"
          :clearable="item.clearable ?? true"
          :popper-append-to-body="false"
          :teleported="false"
          placement="bottom"
          :multiple="item.multiple"
          :collapseTags="item.collapseTags"
          :maxCollapseTags="
            item.collapseTags && !item.maxCollapseTags
              ? 1
              : item.maxCollapseTags
          "
          :collapseTagsTooltip="
            item.collapseTags && !item.collapseTagsTooltip
              ? true
              : item.collapseTagsTooltip
          "
          :placeholder="item.options?.placeholder || '请选择'"
          :disabled="item.disabled"
          v-bind="item.itemProps"
        >
          <el-option
            v-for="s in originOptions?.[item.origin] || item.options?.data"
            :key="s[item.options?.valueKey || 'value']"
            :label="s[item.options?.labelKey || 'label']"
            :value="s[item.options?.valueKey || 'value']"
          />
        </el-select>
      </FormItem>
        <FormItem
        :label="item.label"
        v-else-if="item.type === 'dateTimeRange'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <FormCombined
          v-if="item.select"
          :style="item.full || !_options.inline ? 'flex: 1' : ''"
          :selectStyle="item.selectStyle || 'width: 100px'"
          :options="item.select"
          :modelValue="model[item.field]"
          @update:modelValue="handleCombined(item, $event)"
        >
          <template #default="{ value, input }">
            <el-date-picker
              :modelValue="value"
              :style="item.style"
              type="datetimerange"
              :format="item.format || 'YYYY-MM-DD HH:mm:ss'"
              :valueFormat="item.format || 'YYYY-MM-DD HH:mm:ss'"
              :shortcuts="item.options?.shortcuts"
              :placeholder="item.options?.placeholder"
              @update:modelValue="input"
              :default-time="[new Date(2000, 1, 1, 0, 0, 0),new Date(2000, 2, 1, 23, 59, 59),]"
              v-bind="item.itemProps"
            />
          </template>
        </FormCombined>
        <el-date-picker
          v-else
          v-model="model[item.field]"
          :style="item.style"
          type="datetimerange"
          :format="item.format || 'YYYY-MM-DD HH:mm:ss'"
          :valueFormat="item.format || 'YYYY-MM-DD HH:mm:ss'"
          :shortcuts="item.options?.shortcuts"
          :placeholder="item.options?.placeholder"
          :default-time="[new Date(2000, 1, 1, 0, 0, 0),new Date(2000, 2, 1, 23, 59, 59),]"
          v-bind="item.itemProps"
        />
      </FormItem>
      <FormItem
        :label="item.label"
        v-else-if="item.type === 'switch'"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
      >
        <el-switch v-model="model[item.field]" v-bind="item.itemProps" />
      </FormItem>
      <FormItem
        :label="item.label"
        :rules="item.rules"
        :prop="[item.field]"
        :tooltip="item.tooltip"
        :show="getShow(item)"
        v-else
      >
        <FormCombined
          :style="item.full || !_options.inline ? 'flex: 1' : ''"
          :selectStyle="item.selectStyle || 'width: 100px'"
          :options="item.select"
          :modelValue="model[item.field]"
          @update:modelValue="handleCombined(item, $event)"
        >
          <template #default="{ value, input }">
            <el-input
              :modelValue="value"
              @input="input"
              :style="item.style"
              :readonly="item.readonly"
              :type="item.type ?? 'text'"
              :autosize="item.autosize ?? { minRows: 1, maxRows: 3 }"
              :showPassword="item.showPassword"
              :clearable="item.clearable"
              :placeholder="item.placeholder || `请输入${item.label}`"
              :disabled="item.disabled"
            >
              <template #append v-if="item.append">
                <slot :name="item.append"></slot>
              </template>
            </el-input>
          </template>
        </FormCombined>
        <slot :name="item.field + '-slot'"></slot>
      </FormItem>
    </template>
    <div
      v-if="_options.option"
           class="ml-[auto] pb-[16px]"
      :class="{
        'flex justify-between w-[100%]': optionFull || slots.buttonRight,
      }"
    >
      <div></div>
      <div class="flex items-center">
        <slot name="buttons" :model="model" :formRef="formRef">
          <slot name="buttonLeft"></slot>
          <el-button v-if="_options.showResetButton" @click="resetForm">
            {{ _options.resetButtonText }}
          </el-button>
          <el-button v-if="_options.showCancelButton" @click="emit('cancel')">
            {{ _options.cancelButtonText }}
          </el-button>
          <el-button
            :class="{ 'w-full': options?.blockSubmitButton }"
            type="primary"
            @click="onSubmit"
            >{{ _options.submitButtonText }}
          </el-button>
          <slot name="buttonRight"></slot>
        </slot>
      </div>
    </div>
  </el-form>
</template>
<script setup>
import { ref } from 'vue'
import FormTitle from '@/components/Form/FormTitle.vue'
import FormItem from '@/components/Form/FormItem.vue'
import FormCombined from '@/components/Form/FormCombined'
import NumberRange from '@/components/NumberRange/index.vue'

import { overallOptions } from '@/api/public'

const slots = useSlots();

const model = ref({})
const formRef = ref()
const originOptions = ref({})
const props = defineProps({
  fieldList: {
    /**
     * {
        label?: string,
        labelWidth?: string | number, // 标签宽度，例如 '50px'。 可以使用 auto。
        field: string,
        type?: ItemType,
        value?: any,
        placeholder?: string,
        disabled?: boolean,
        readonly?: boolean,
        options?: IFieldOptions,
        rules?: import('element-plus').FormItemRule[]
        clearable?: boolean // 是否可清空
        showPassword?: boolean, // 是否显示切换密码图标
        enterable?: boolean, // 当为输入框时，是否启用回车触发提交功能
      }[]
     */
    type: Array,
    default: () => [],
  },
  model: {
    type: Object,
    default: () => ({}),
  },
  options: {
    /**
      * {
        labelWidth?: string | number,
        labelPosition?: 'left' | 'right' | 'top',
        disabled?: boolean,
        size?: 'large' | 'small' | 'default',
        showResetButton?: boolean, // 是否展示重置按钮
        showCancelButton?: boolean, // 是否展示取消按钮
        submitButtonText?: string,
        resetButtonText?: string,
        cancelButtonText?: string,
        blockSubmitButton?: boolean // 提交按钮是否以块级按钮呈现
    }
   */
    type: Object,
    default: () => ({}),
  },
})

const emit = defineEmits(['submit', 'cancel', 'reset', 'change'])

const _options = computed(() => {
  const option = {
    // labelWidth: 'auto',
    labelPosition: 'right',
    disabled: false,
    scrollToError: true,
    scrollIntoViewOptions: true,
    submitButtonText: '查询',
    resetButtonText: '重置',
    cancelButtonText: '取消',
  }
  return Object.assign(option, props?.options)
})

const updateSlotValue = field => e => {
  model.value[field] = e
}

const handleCombined = (item, e) => {
  const { key, value } = e || {}
  if (item.select) {
    item.select.forEach(n => {
      model.value[n.value] = undefined
    })
    if (!key) return
    model.value[key] = value
    model.value[item.field] = { key, value }
  } else {
    model.value[item.field] = value ? JSON.parse(JSON.stringify(value)) : value
  }
}

const updateNumberRange = (field, val) => {
  model.value[field] = val
}

const getShow = row => {
  const { show = () => true } = row
  return show ? show?.({ row, model: model.value }) : true
}

const handleGetOriginOptions = async originArr => {
  try {
    const optionsParams = originArr.map(n => n.origin)
    try {
      const res = await overallOptions({
        params: optionsParams,
      })
      const { data } = res
      for (let item in data) {
        originOptions.value[item] = data[item]
      }
    } catch (e) {
      console.error(e)
    }
  } catch (e) {
    console.error(e)
  }
}

const handleDefault = () => {
  props.fieldList.map(item => {
    // 如果类型为checkbox，默认值需要设置一个空数组
    const value = item.type === 'checkbox' ? [] : ''
    props.model
      ? (model.value = JSON.parse(JSON.stringify(props.model)))
      : (model.value[item.field] = item.value || value)
  })
}
handleDefault()

watch(
  () => model.value,
  val => {
    emit('change', val)
  },
  { deep: true }
)

watch(
  () => props.model,
  val => {
    if (val && Object.keys(val).length) {
      handleDefault()
    }
  }
)

watch(
  () => props.fieldList,
  () => {
    const originArr = props.fieldList.filter(n => n.origin)
    if (originArr?.length) {
      handleGetOriginOptions(originArr)
    }
  },
  { immediate: true, deep: true }
)

const onSubmit = () => {
  formRef.value.validate(valid => {
    const _model = JSON.parse(JSON.stringify(model.value))
    if (valid) {
      emit('submit', _model)
      return false
    }
  })
}

const submit = () => {
  return new Promise((resolve, reject) => {
    formRef.value.validate(valid => {
      const _model = JSON.parse(JSON.stringify(model.value))
      if (valid) {
        resolve(_model)
      } else {
        reject(false)
      }
    })
  })
}

const handleKeyUp = enterable => {
  if (!enterable) return
  onSubmit(formRef.value)
}

const resetForm = () => {
  model.value = JSON.parse(JSON.stringify(props.model))
  formRef.value.resetFields()
  emit('reset')
}

defineExpose({
  formRef,
  onSubmit,
  resetForm,
  submit,
})
</script>
<style lang="scss" scoped>
.form-components {
    display: flex;
  flex-wrap: wrap;
  &.line {
    border-bottom: 1px solid #eff3fe;
  }
  :deep(.el-form-item--default) {
    margin-bottom: 8px;
    margin-right: 8px;
  }
  :deep(.el-input-group) {
    .el-input__wrapper {
      border-top-right-radius: 0;
      border-bottom-right-radius: 0;
    }
  }
}
</style>
